home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 58678 / 58678.xpi / chrome / clipple.jar / content / clipple.js < prev    next >
Text File  |  2010-01-20  |  14KB  |  389 lines

  1. /**
  2.  * @fileOverview Clipple - Provides multiple clipboards to mozilla apps
  3.  * @name clipple.js
  4.  * @author mooz <stillpedant@gmail.com>
  5.  * @license The MIT License
  6.  */
  7.  
  8. let Clipple =
  9.     (function () {
  10.          // Private {{ =============================================================== //
  11.  
  12.          const Cc = Components.classes;
  13.          const Ci = Components.interfaces;
  14.  
  15.          let _windowType = window.document.documentElement.getAttribute("windowtype");
  16.          let _ignoreNextCommand = false;
  17.  
  18.          let clip, util;
  19.  
  20.          function init() {
  21.              // load modules
  22.              try
  23.              {
  24.                  Components.utils.import("resource://clipple-share/share.js", self.modules);
  25.  
  26.                  clip = self.modules.clip;
  27.                  util = self.modules.util;
  28.              }
  29.              catch (x)
  30.              {
  31.                  return;
  32.              }
  33.  
  34.              // arrange information of this extension
  35.              let extmanager = Cc["@mozilla.org/extensions/manager;1"].createInstance(Ci.nsIExtensionManager);
  36.              self.extInfo = extmanager.getItemForID(self.id);
  37.  
  38.              // for window which does not have goDoCommand()
  39.              if (typeof goDoCommand === 'undefined')
  40.              {
  41.                  window.goDoCommand = function (aCommand)
  42.                  {
  43.                      try
  44.                      {
  45.                          let controller = top.document.commandDispatcher.getControllerForCommand(aCommand);
  46.                          if (controller && controller.isCommandEnabled(aCommand))
  47.                              controller.doCommand(aCommand);
  48.                      }
  49.                      catch (e)
  50.                      {
  51.                          util.message("An error %s occurred executing the %s command", e, aCommand);
  52.                      }
  53.                  };
  54.              }
  55.  
  56.              // Main window
  57.              if (!self.isThunderbird && _windowType === "navigator:browser")
  58.              {
  59.                  hookContentAreaContextMenu();
  60.  
  61.                  // hook location bar copy / cut event
  62.                  try
  63.                  {
  64.                      let controller        = document.getElementById("urlbar")._copyCutController;
  65.                      let originalDoCommand = controller.doCommand;
  66.  
  67.                      controller.doCommand = function (aCommand) {
  68.                          originalDoCommand.apply(this, arguments);
  69.                          self.copyCommandCalled();
  70.                      };
  71.                  }
  72.                  catch (x)
  73.                  {
  74.                      util.message(x);
  75.                  }
  76.              }
  77.  
  78.              // hook contextmenu event
  79.              hookGlobalContextMenu();
  80.  
  81.              // hook copy event
  82.              hookCopyCommand();
  83.          }
  84.  
  85.          // }} ======================================================================= //
  86.  
  87.          function hookCopyCommand() {
  88.              window.addEventListener(
  89.                  "copy", function () {
  90.                      // we need to insert delay before calling this function
  91.                      setTimeout(function () { self.copyCommandCalled(); }, 0);
  92.                  },
  93.                  false
  94.              );
  95.          }
  96.  
  97.          const clipplePasteMultipleMenuClass = "clipple-paste-multiple-menu";
  98.          const clipplePasteMultipleItemClass = "clipple-paste-multiple-item";
  99.  
  100.          function createPopup() {
  101.              let popup = document.createElement("menupopup");
  102.              popup.setAttribute("class", clipplePasteMultipleMenuClass);
  103.  
  104.              for (let [i, text] in Iterator(clip.ring))
  105.              {
  106.                  let menuItem = document.createElement("menuitem");
  107.                  menuItem.setAttribute("label", (i + 1) + ". " + text);
  108.                  menuItem.setAttribute("value", text);
  109.                  menuItem.setAttribute("tooltiptext", text);
  110.                  menuItem.setAttribute("class", clipplePasteMultipleItemClass);
  111.  
  112.                  popup.appendChild(menuItem);
  113.              }
  114.  
  115.              return popup;
  116.          }
  117.  
  118.          function updateMenu(aMenu) {
  119.              let popup = createPopup();
  120.  
  121.              if (aMenu.firstChild)
  122.                  aMenu.replaceChild(popup, aMenu.firstChild);
  123.              else
  124.                  aMenu.appendChild(popup);
  125.          }
  126.  
  127.          function implantClipple(aContextMenu, aOnPopUp) {
  128.              const pasteIcon = "chrome://clipple/skin/icon16/paste.png";
  129.  
  130.              let items = Array.slice(aContextMenu.getElementsByTagName("menuitem"), 0)
  131.                  .concat(Array.slice(aContextMenu.getElementsByTagName("xul:menuitem"), 0));
  132.  
  133.              let itemPaste;
  134.  
  135.              items.some(function (elem) {
  136.                             if (typeof elem.getAttribute === "function" &&
  137.                                 (elem.getAttribute("command") === "cmd_paste" ||
  138.                                  elem.getAttribute("cmd")     === "cmd_paste"))
  139.                             {
  140.                                 itemPaste = elem;
  141.                                 return true;
  142.                             }
  143.  
  144.                             return false;
  145.                         });
  146.  
  147.              let menu = document.createElement("menu");
  148.              menu.setAttribute("label", util.getLocaleString("clipplePaste"));
  149.              menu.setAttribute("accesskey", "l");
  150.              menu.setAttribute("class", "menu-iconic");
  151.              menu.setAttribute("image", pasteIcon);
  152.  
  153.              if (itemPaste)
  154.                  itemPaste.parentNode.insertBefore(menu, itemPaste.nextSibling);
  155.              else
  156.                  aContextMenu.appendChild(menu);
  157.  
  158.              menu.addEventListener("command", function (ev) {
  159.                                        let text = ev.target.getAttribute("value");
  160.                                        if (text)
  161.                                            util.insertText(text, document);
  162.                                    }, false);
  163.  
  164.              aContextMenu.addEventListener("popupshowing", aOnPopUp, false);
  165.  
  166.              return menu;
  167.          }
  168.  
  169.          function climbNodes(aNode, aMaxStairs, aProsess) {
  170.              for (let i = 0; i < aMaxStairs; ++i)
  171.              {
  172.                  aNode = aNode.parentNode;
  173.                  if (aProsess(aNode))
  174.                      return aNode;
  175.              }
  176.  
  177.              return null;
  178.          }
  179.  
  180.          function hookGlobalContextMenu() {
  181.              document.addEventListener(
  182.                  "contextmenu",
  183.                  function (ev) {
  184.                      let target = document.commandDispatcher.focusedElement;
  185.  
  186.                      function hackInputBoxContextMenu() {
  187.                          let inputBox = climbNodes(target, 4, function (node)
  188.                                                    ((node.getAttribute("class") || "").indexOf("textbox-input-box") >= 0));
  189.  
  190.                          let contextMenu = document.getAnonymousElementByAttribute(
  191.                              inputBox,
  192.                              "anonid", "input-box-contextmenu"
  193.                          );
  194.  
  195.                          if (contextMenu && !contextMenu.__clippleHooked__)
  196.                          {
  197.                              let itemPasteMultiple;
  198.  
  199.                              function onGlobalPopup(aEvent) {
  200.                                  if (aEvent)
  201.                                  {
  202.                                      let elem = aEvent.target;
  203.  
  204.                                      if ((elem.getAttribute("class") || "").indexOf(clipplePasteMultipleMenuClass) >= 0)
  205.                                          return;
  206.                                  }
  207.  
  208.                                  clip.sync();
  209.  
  210.                                  if (!clip.ring.length)
  211.                                  {
  212.                                      itemPasteMultiple.setAttribute("disabled", true);
  213.                                  }
  214.                                  else
  215.                                  {
  216.                                      itemPasteMultiple.removeAttribute("disabled");
  217.                                      updateMenu(itemPasteMultiple);
  218.                                  }
  219.                              }
  220.  
  221.                              itemPasteMultiple = implantClipple(contextMenu, onGlobalPopup);
  222.                              contextMenu.__clippleHooked__ = true;
  223.  
  224.                              onGlobalPopup();
  225.                          }
  226.                      }
  227.  
  228.                      if (target)
  229.                          setTimeout(hackInputBoxContextMenu, 10);
  230.                  }, true);
  231.          }
  232.  
  233.          function hookContentAreaContextMenu() {
  234.              let itemPasteMultiple;
  235.  
  236.              function onContentPopup(aEvent) {
  237.                  if (aEvent.target.localName === "menupopup")
  238.                      return;
  239.  
  240.                  if (!gContextMenu.onTextInput)
  241.                  {
  242.                      itemPasteMultiple.hidden = true;
  243.                  }
  244.                  else
  245.                  {
  246.                      clip.sync();
  247.  
  248.                      if (!clip.ring.length)
  249.                      {
  250.                          itemPasteMultiple.setAttribute("disabled", true);
  251.                      }
  252.                      else
  253.                      {
  254.                          itemPasteMultiple.removeAttribute("disabled");
  255.                          updateMenu(itemPasteMultiple);
  256.                      }
  257.  
  258.                      itemPasteMultiple.hidden = false;
  259.                  }
  260.              }
  261.  
  262.              let contextMenu = document.getElementById("contentAreaContextMenu");
  263.              itemPasteMultiple = implantClipple(contextMenu, onContentPopup);
  264.          }
  265.  
  266.          function isPassword(aElem) {
  267.              if (!aElem)
  268.                  return false;
  269.  
  270.              return (aElem.localName.toLowerCase() === "input" &&
  271.                      "getAttribute" in aElem &&
  272.                      aElem.getAttribute("type") === "password");
  273.          }
  274.  
  275.          // }} ======================================================================= //
  276.  
  277.          // Public {{ ================================================================ //
  278.  
  279.          let self = {
  280.              modules: {
  281.  
  282.              },
  283.  
  284.              get version() {
  285.                  return _extInfo.version;
  286.              },
  287.  
  288.              get id() {
  289.                  return "clipple@mooz.github.com";
  290.              },
  291.  
  292.              get isThunderbird() {
  293.                  return !!window.navigator.userAgent.match(/thunderbird/i);
  294.              },
  295.  
  296.              pasteMultiple: function (ev) {
  297.                  let popup = createPopup();
  298.                  let elem  = (ev || {target : document.commandDispatcher.focusedElement}).target;
  299.  
  300.                  if (elem)
  301.                  {
  302.                      document.documentElement.appendChild(popup);
  303.  
  304.                      popup.addEventListener("command", function (ev) {
  305.                                                 let text = ev.target.getAttribute("value");
  306.                                                 if (text)
  307.                                                     util.insertText(text, document);
  308.  
  309.                                                 popup.removeEventListener("command", arguments.callee, false);
  310.                                             }, false);
  311.  
  312.                      popup.addEventListener("popuphidden", function (ev) {
  313.                                                 popup.removeEventListener("popuphidden", arguments.callee, false);
  314.                                                 document.documentElement.removeChild(popup);
  315.                                             }, false);
  316.  
  317.                      popup.openPopup(elem, "after_end", 0, 0, true);
  318.                  }
  319.              },
  320.  
  321.              copyCommandCalled: function (ev) {
  322.                  let text = util.clipboardGet() || "";
  323.                  let elem = document.commandDispatcher.focusedElement;
  324.  
  325.                  if (!isPassword(elem))
  326.                  {
  327.                      clip.pushText(text);
  328.                  }
  329.                  else
  330.                  {
  331.                      // password
  332.                      if (!util.getBoolPref(util.getPrefKey("ignore_password"), true)
  333.                          && elem.value)
  334.                      {
  335.                          try
  336.                          {
  337.                              let [start, end] = [elem.selectionStart, elem.selectionEnd];
  338.  
  339.                              elem.setAttribute("type", "text");
  340.                              elem.focus();
  341.  
  342.                              let text = elem.value.toString().substring(start, end);
  343.  
  344.                              util.clipboardSet(text);
  345.                              clip.pushText(text);
  346.                          }
  347.                          catch (x)
  348.                          {
  349.                              util.message(x);
  350.                          }
  351.                          finally
  352.                          {
  353.                              elem.setAttribute("type", "password");
  354.                              elem.focus();
  355.                          }
  356.                      }
  357.                      else
  358.                      {
  359.                          util.clipboardSet(clip.ring[0] || "");
  360.                      }
  361.                  }
  362.              },
  363.  
  364.              /**
  365.               * Open preference dialog
  366.               */
  367.              openPreference: function (aForce) {
  368.                  let openedWindow = util.getWindow('Clipple:Preference');
  369.  
  370.                  if (openedWindow && !aForce)
  371.                  {
  372.                      openedWindow.focus();
  373.                  }
  374.                  else
  375.                  {
  376.                      window.openDialog("chrome://clipple/content/preference.xul",
  377.                                        "Preferences",
  378.                                        "chrome,titlebar,toolbar,centerscreen,resizable,scrollbars");
  379.                  }
  380.              }
  381.          };
  382.  
  383.          // }} ======================================================================= //
  384.  
  385.          init();
  386.  
  387.          return self;
  388.      })();
  389.